#include <stdio.h>
#include "../sjjg/list.h"
#include "../sjjg/mem_pool.h"

#define _AUTO_TEST_

#ifdef _AUTO_TEST_
#define PRINT
#else
#define PRINT printf
#endif

size_t rbtree_balance(ms_rbtree_p t, ms_rbtree_p n, size_t d) {
    if (t == n) return d;
    if (MS_RBTREE_BLACK == n->color)
        ++d;
    else {
        if (n->left->color == MS_RBTREE_RED || n->right->color == MS_RBTREE_RED) {
            printf("rbtree red error\n");
        }
    }
    size_t ld = rbtree_balance(t, n->left, d);
    size_t rd = rbtree_balance(t, n->right, d);
    if (ld != rd) {
        printf("rbtree black error\n");
    }
    return ld;
}

void show(ms_mpool mp) {
    size_t use_count = 0, free_count = 0;
    ms_mpool_memory top = mp->mem_list;
    do
    {
        PRINT("%p:", top);
        ms_mpool_block block = (ms_mpool_block)(top + 1);
        while (block) {
            if (block->b_free) {
                ++free_count;
                PRINT("%p free:%lu\t", block, block->size);
            }
            else {
                ++use_count;
                PRINT("%p used:%lu\t", block, block->size);
            }
            block = block->next;
        }
        PRINT("\n");
        top = top->next;
    } while (top != mp->mem_list);
    printf("use_count:%lu\t", use_count);
    printf("free_count:%lu == %lu\n", free_count, mp->free_blocks->key);
    size_t up_size = 0;
    ms_rbtree_p n = ms_rbtree_begin(mp->free_blocks);
    while (n != ms_rbtree_end(mp->free_blocks)) {
        ms_mpool_block block = (ms_mpool_block)n - 1;
        PRINT("%p free:%lu ", n, n->key);
        if (block->size != n->key) {
            printf("\nsize error.%lu > %lu", block->size, n->key);
            break;
        }
        if (up_size > n->key) {
            printf("\nsize sort error.%lu > %lu", up_size, n->key);
            break;
        }
        else {
            up_size = n->key;
        }
        n = ms_rbtree_next(mp->free_blocks, n);
    }
    PRINT("\n");
    rbtree_balance(mp->free_blocks, mp->free_blocks->parent, 0);
}

size_t getV(const char* txt) {
    size_t a = rand() % 10000;
    PRINT("%s:%lu\n", txt, a);
    return a;
}

char getChar() {
    char a = rand() % 10;
    PRINT("%c\n", a + '0');
    return a + '0';
}

int getSel() {
    printf("1.malloc\n");
    printf("2.free\n");
    printf("3.realloc\n");
    printf("4.clear\n");
    printf("5.show\n");
    printf("other exit\n");
    printf("select please:");
    int i;
    scanf("%d", &i);
    return i;
}

typedef struct alc {
    char* p;
    size_t size;
    char c;
}ALC;
void alc_copy(void* self, void* data) {
    memcpy(self, data, sizeof(ALC));
}
bool isAlc(ALC* pAlc) {
    for (size_t i = 0;i < pAlc->size;++i) {
        if (pAlc->p[i] != pAlc->c) {
            printf("%lu,%c:%lu=%s\n", pAlc->size, pAlc->c, i, pAlc->p + i);
            return false;
        }
    }
    return true;
}
int main(int args, char* argv[]) {
    ms_mpool mp = ms_mpool_create(0);
    ms_list_funs funs = {
        NULL, NULL, alc_copy
    };
    show(mp);
    ms_list_t* list = ms_list_create((ms_alloc_t*)mp, sizeof(ALC), &funs);
    //ms_list_t* list = ms_list_create(NULL, sizeof(ALC), &funs);
    if (mp) {
        size_t i = 0;
        do
        {
            size_t a = 0;
            void* p = NULL;
            void* new_p = NULL;
            ALC v;
            ms_mpool_block block = NULL;
            //int sel = getSel();
            int sel = 0;
#ifdef _AUTO_TEST_
            if (i % 2) {
                sel = rand() % 9;
                switch (sel)
                {
                case 0:
                case 1:
                    sel = 1;
                    break;
                case 2:
                case 3:
                    sel = 2;
                    break;
                case 4:
                    if (ms_mpool_total_size(mp) < 256 * 1024 * 1024) {
                        sel = 1;
                    }
                    else {
                        sel = 3;
                    }
                    break;
                case 5:
                    if (ms_mpool_total_size(mp) > 512 * 1024 * 1024) {
                        sel = 2;
                    }
                    else {
                        sel = 3;
                    }
                    break;
                case 6:
                case 7:
                case 8:
                    sel = 3;
                    break;
                default:
                    break;
                }
                PRINT("!!!!!!select:%d\n", sel);
            }
            else {
                sel = 5;
            }
#else
            sel = getSel();
#endif
            switch (sel) {
            case 1:
                a = getV("malloc a block");
                p = ms_mpool_malloc(mp, a);
                v.c = getChar();
                memset(p, v.c, a);
                block = (ms_mpool_block)p - 1;
                PRINT("%lX %lu %lu\n", p, block->size, block->b_free);
                v.p = (char*)p;
                v.size = a;

                ms_list_push_back(list, &v);
                break;
            case 2:
                if (!ms_list_empty(list)) {
                    ALC* pAlc = (ALC*)ms_list_front(list);
                    isAlc(pAlc);
                    block = (ms_mpool_block)pAlc->p - 1;
                    PRINT("%lu %lu\n", block->size, block->b_free);
                    ms_mpool_free(mp, pAlc->p);
                    ms_list_pop_front(list);
                }
                break;
            case 3:
                a = getV("realloc a block");
                if (ms_list_size(list) > 0) {
                    ALC* pAlc = (ALC*)ms_list_front(list);
                    isAlc(pAlc);
                    block = (ms_mpool_block)pAlc->p - 1;
                    PRINT("old: %lu %lu\n", block->size, block->b_free);
                    new_p = ms_mpool_realloc(mp, pAlc->p, a);
                    if (new_p) {
                        v.c = getChar();
                        memset(new_p, v.c, a);
                        ms_list_pop_front(list);
                        v.p = (char*)new_p;
                        v.size = a;
                        ms_list_push_back(list, &v);
                        block = (ms_mpool_block)new_p - 1;
                        PRINT("new: %lu %lu\n", block->size, block->b_free);
                    }
                }
                break;
            case 4:
                ms_mpool_clear(mp);
                ms_list_clear(list);
                break;
            case 5:
                show(mp);
                break;
            default:
                goto exit;
            }
        } while (++i < 987654321);
    exit:
        printf("exit\n");
        ms_mpool_destory(mp);
    }
    return 0;
}
